<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>
        <input type="text" id="ipt">
    </div>
    <script>

        // // 模仿new关键字
        function mynew(func, ...args) {
            const obj = {}
            // 新对象原型指向构造函数原型对象，确保 obj 可以访问到构造函数 func 原型链上的属性和方法。 对象是__proto__,函数是prototype
            obj.__proto__ = func.prototype
            // 将 func 函数的 this 绑定到 obj 上，以及传入的参数 args   
            let result = func.apply(obj, args)
            // 判断 result 是否是一个对象，如果是对象则直接返回 result，否则返回 obj。这样确保了在构造函数有显式返回对象时返回该对象，否则返回新创建的 obj。
            return result instanceof Object ? result : obj
        }
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        Person.prototype.say = function () {
            console.log(this.name)
        }
        let p = mynew(Person, "hhh", 123)
        console.log(p)
        p.say()


        // function fn(...args) {
        //     console.log(this, args);
        // }
        // let obj = {
        //     name: "bug"
        // }
        // const bindFn = fn.bind(obj); //this变为obj，且不会立马执行
        // bindFn(1, 2) //得通过调用才会执行，并传入参数列表1,2，最终this指向obj {name:'bug'}
        // fn(1, 2) //this执行window
        // const obj={
        //     name:'xxx',
        //     age:20
        // }

        var obj = {
            age: 18,
            person: {
                name1: 'fx',
                name2: 'xka'
            },
            list: ['hhh', '666'],
            love: function () {
                console.log('嘿嘿')
            }
        }
        // 手写浅拷贝
        function clone(object) {
            const newObj = {}
            for (let proto in object) {
                if (object.hasOwnProperty(proto)) {
                    newObj[proto] = object[proto]
                }
            }
            // console.log(object)
            // console.log(newObj)
            return newObj
        }
        // const obj1 = clone(obj)
        // console.log(obj)
        // console.log(obj1)

        // 手写深拷贝
        function deepClone(obj, hash = new WeakMap()) {
            // 数据过滤
            if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作 
            if (obj instanceof Date) return new Date(obj);// 如果传入的对象是日期对象，使用 new Date() 创建一个新的日期对象并返回
            if (obj instanceof RegExp) return new RegExp(obj);// 如果传入的对象是正则表达式对象，使用 new RegExp() 创建一个新的正则表达式对象并返回
            // 如果传入的对象不是普通对象（即不是对象类型）或普通的值 如果是函数的话就不需要深拷贝
            // 因为拷贝的只需要考虑是否为对象，所以只需要判断obj是否为对象类型即可，因为null或者undefined在上面已经先过滤掉了，此时就只剩下基本数据类型和函数了
            if (typeof obj !== "object") return obj;
            // 来到此处的话就只剩下对象了，就要进行深拷贝
            if (hash.get(obj)) return hash.get(obj);
            // 深拷贝
            // 创建一个新对象，这个新对象和obj对象类型相同
            // 找到的是所属类原型上的constructor属性，而原型上的constructor指向的是当前类本身
            let cloneObj = new obj.constructor();
            hash.set(obj, cloneObj);
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    // 实现一个递归拷贝
                    cloneObj[key] = deepClone(obj[key], hash);
                }
            }
            return cloneObj;
        }

        // assign浅拷贝
        // var obj = {
        //     age: 18,
        //     names: {
        //         name1: 'fx',
        //         name2: 'xka'
        //     },
        //     list:['hhh','666'],
        //     love: function () {
        //         console.log('嘿嘿')
        //     }
        // }
        // var newObj = Object.assign({}, obj);
        // //因为是浅拷贝，所以只拷贝了基本类型的，引用类型还是共享内存地址的，即改变obj的应用类型的内容，newObj里面的引用类型的值也随之改变
        // obj.names.name1='xxx' 
        // obj.list[0]='xxx' 
        // console.log(newObj)

        // const fxArr = ["One", {
        //     name: "Two",
        //     age: 20
        // }, "Three"]
        // const fxArrs = fxArr.concat()
        // fxArr[1].name = "four";
        // console.log(fxArrs[1].name) //four

        // const fxArr = ["One", {
        //     name: "Two",
        //     age: 20
        // }, "Three"]
        // const fxArrs = fxArr.slice(0,)
        // fxArr[1].name = "four";
        // console.log(fxArrs[1].name) //four

        // const fxArr = ["One", {
        //     name: "Two",
        //     age: 20
        // }, "Three"]
        // const fxArrs = [...fxArr]
        // fxArr[1].name = "four";
        // console.log(fxArrs[1].name) //four

        // const obj1 = {
        //     name: 'init',
        //     arr: [1, [2, 3], 4],
        //     fn:function(){
        //         this.name='init'
        //     }
        // };
        // const obj2 = deepClone(obj1) // 深拷贝
        // const obj3 = Object.assign({}, obj1); // 浅拷贝
        // obj1.arr[0] = 5;
        // console.log(obj2)
        // console.log(obj3)

        // 深拷贝
        // var obj = {
        //     age: 18,
        //     person: {
        //         name1: 'fx',
        //         name2: 'xka'
        //     },
        //     list:['hhh','666'],
        //     love: function () {
        //         console.log('嘿嘿')
        //     }
        // }

        // const obj2=JSON.parse(JSON.stringify(obj));
        // obj.person.name1='6666'
        // console.log(obj2.person.name1) //fx


        // const obj2 = deepClone(obj) // 深拷贝
        // const obj3 = Object.assign({}, obj) // 浅拷贝
        // const obj4 = clone(obj) // 浅拷贝
        // obj.person.name1 = 'hhh';
        // //因为是深拷贝，obj2中的引用类型新开辟了一个内存地址，所以obj的person改变obj2不受影响
        // console.log(obj2.person.name1) //fx
        // //因为是浅拷贝，obj3、obj4中的引用类型与obj中的引用类型共享内存地址，所以obj的person改变obj3、obj4皆受影响
        // console.log(obj3.person.name1) //hhh
        // console.log(obj4.person.name1) //hhh
        function Foo() {
            Foo.a = function () {
                console.log(1);
            };
            this.a = function () {
                console.log(2);
            };
        }
        Foo.prototype.a = function () {
            console.log(3);
        };
        Foo.a = function () {
            console.log(4);
        };
        Foo.a();
        let obj2 = new Foo();
        obj2.a();
        Foo.a();
    </script>



</body>

</html>